﻿using System;
using System.Collections.Generic;
using System.Text;
using Mighty.Common;
using log4net;
using log4net.Config;

namespace Mighty.Brain
{
    public class BossBrain : BasicBrain
    {
        private BossData bossData;
        private static readonly ILog log = LogManager.GetLogger(typeof(BossBrain));

        public BossBrain()
        {
        }

        public override void GameStartEvent()
        {
            bossData = new BossData();
        }

        public override void BettingReadyEvent()
        {
            bossData.InitData(gameInfo);
            bossData.UpdateMyCard();

            int[] quality = new int[] { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 6, 10 };       // 15 (4장조건 3턴윈:20, 2턴윈:16) 
            int[] quantity = new int[] { 0, 0, 9, 13, 14, 16, 18, 20, 20, 20, 20 };           // 10 (AQ조건에서 양적보너스)

            int[] myQuality = new int[4] { 0, 0, 0, 0 };
            int[] myQuantity = new int[4] { 0, 0, 0, 0 };

            for ( int i = 0 ; i < 10 ; i++ ) {
                HandedCard h  = gameInfo.myCards[i];
                int shape = (int)h.shape;
                if (h.shape == Shape.Joker)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        myQuality[j] += 6;                                                    // joker는 K로 취급
                        myQuantity[j]++;
                    }
                }
                else
                {
                    myQuality[shape] += quality[h.number];
                    myQuantity[shape]++;
                }
            }
            for (int i = 0; i < 4; i++) if (myQuality[i] > 20) myQuality[i] = 20;

            // 가장 높은 score를 가지는 Shape를 선택
            bossData.highShape = Shape.Clover;
            bossData.highScore = -1;
            for (int i = 0; i < 4; i++)
            {
                int scoreQuality = myQuality[i];
                int scoreQuantity = quantity[myQuantity[i]];
                if (scoreQuality + scoreQuantity == 0) continue;

                int score = (2 * scoreQuality * scoreQuantity / (scoreQuality + scoreQuantity));  // 조화평균
                if (score > bossData.highScore)
                {
                    bossData.highScore = score;
                    bossData.highShape = (Shape)i;
                }
            }

            // 마이티를 가지면 +2
            if ((bossData.highShape == Shape.Spade && bossData.usedCard[(int)Shape.Diamond, 14] == UsedType.have) || (bossData.highShape != Shape.Spade && bossData.usedCard[(int)Shape.Spade, 14] == UsedType.have))
            {
                bossData.usedMighty = UsedType.have;
                bossData.highScore += 2;
            }

            // 초구를 안가지면 -1 or -2
            UpdateNeedFirstWin();
            if (bossData.needFirstWin)
            {
                bossData.highScore -= 1;
                if (bossData.highScore > 16) bossData.highScore -= 1; // 높으면 초구의 위력이 커진다
            }
          
        }

        private void UpdateNeedFirstWin()
        {
            bool firstCard = false;
            for (int i = 0; i < 4; i++)
            {
                if ( i == (int)bossData.highShape ) continue;                                       // 기루다무늬x
                else if (bossData.highShape == Shape.Spade && i == (int)Shape.Diamond) continue;    // 빨간마이티x
                else if (bossData.highShape != Shape.Spade && i == (int)Shape.Spade) continue;      // 까만마이티x
                if (bossData.usedCard[i, 14] == UsedType.have) firstCard = true;    
            }
            if (bossData.highShape == Shape.Spade && bossData.usedCard[(int)Shape.Diamond, 13] == UsedType.have) firstCard = true;      // 다이아 k
            else if (bossData.highShape != Shape.Spade && bossData.usedCard[(int)Shape.Spade, 13] == UsedType.have) firstCard = true;   // 스페이드 k

            if (!firstCard) bossData.needFirstWin = true;
            else bossData.needFirstWin = false;
        }

        public override Betting BettingTicEvent()
        {
            Betting betting = new Betting();
            betting.isPass = true;

            int prevLimit = 12;
            if (gameInfo.betting.limit >= 13 ) {
                prevLimit = gameInfo.betting.limit;
            }

            if (prevLimit < bossData.highScore)
            {
                betting.isPass = false;
                betting.giruda = (Giruda)bossData.highShape;
                betting.limit = prevLimit + 1;
                if (betting.limit + 2 < bossData.highScore) betting.limit = bossData.highScore - 2; // 최대Limit에서 2를 빼서 부르기 시작한다
            }
            return betting;
        }

        public override void BettingCompleteEvent()
        {
            bossData.InitData(gameInfo);
            bossData.UpdateMyCard();
        }

        public override HandedCard[] ChoiceCardEvent()
        {
            int[] cardScore = new int[13] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

            // 기루다 & 특수카드
            for (int i = 0; i < 13; i++)
            {
                HandedCard h = gameInfo.myCards[i];

                // 기루다
                if ((int)h.shape == (int)gameInfo.betting.giruda) cardScore[i] = 10;

                // 특수카드
                if (h.shape == Shape.Joker) cardScore[i] = 10;
                if (h.IsSame(gameInfo.mighty)) cardScore[i] = 10;
            }

            // 윈카드남기기
            for (int i = 0; i < 4; i++)
            {
                for (int j = 14; j > 1; j--)
                {
                    if (bossData.usedCard[i, j] == UsedType.have)
                    {
                        int idx = bossData.indexCard[i, j];
                        if (cardScore[idx] < 5) cardScore[idx] = 5;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            // 제일많은무늬남기기
            int[] countCard = new int[4] { bossData.cntInitShape[0], bossData.cntInitShape[1], bossData.cntInitShape[2], bossData.cntInitShape[3] };
            int[] shapeIndex = new int[4] { 0, 1, 2, 3 };
            Array.Sort(countCard, shapeIndex);

            for (int i = 0; i < 2; i++)
            {
                int shape = shapeIndex[i];
                for (int j = 0; j < 15; j++)
                {
                    int idx = bossData.indexCard[shape, j];
                    if (idx == -1) continue;
                    if (cardScore[idx] <= 0) cardScore[idx] = -2 + i;
                }
            }

            // 정렬하기
            int[] cardIndex = new int[13] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
            Array.Sort(cardScore, cardIndex); 
            for (int i = 0; i < 3; i++)
            {
                int idx = cardIndex[i];
                gameInfo.myCards[idx].cmdRemove = true;
            }
            for (int i = 3; i < 13; i++)
            {
                int idx = cardIndex[i];
                gameInfo.myCards[idx].cmdRemove = false;
            }

            UpdateNeedFirstWin();
            return gameInfo.myCards;
        }


        public override FriendPlayer ChoiceFriendEvent()
        {
            bossData.InitData(gameInfo);
            bossData.UpdateMyCard();

            FriendPlayer f = new FriendPlayer();
            if (bossData.usedMighty != UsedType.have)
            {
                f.howToSelect = FriendPlayer.HowToSelect.card;
                f.cardCondition = gameInfo.mighty;
            }
            else if (bossData.usedJoker != UsedType.have)
            {
                f.howToSelect = FriendPlayer.HowToSelect.card;
                f.cardCondition = new Card(Shape.Joker, 0);
            }
            else if (bossData.needFirstWin)
            {
                f.howToSelect = FriendPlayer.HowToSelect.firstwin;
            }
            else
            {
                int shape = (int)gameInfo.betting.giruda;
                if (bossData.usedCard[shape, 14] == UsedType.have
                    && bossData.usedCard[shape, 13] == UsedType.have
                    && bossData.usedCard[shape, 12] == UsedType.have)
                {
                    f.howToSelect = FriendPlayer.HowToSelect.nofriend;
                }
                else
                {
                    f.howToSelect = FriendPlayer.HowToSelect.card;
                    if (bossData.usedCard[shape, 14] != UsedType.have) f.cardCondition = new Card((Shape)shape, 14);
                    else if (bossData.usedCard[shape, 13] != UsedType.have) f.cardCondition = new Card((Shape)shape, 13);
                    else if (bossData.usedCard[shape, 12] != UsedType.have) f.cardCondition = new Card((Shape)shape, 12);
                }
            }
            return f;
        }


        public override Betting LastBettingEvent()
        {
            bossData.InitData(gameInfo);
            bossData.UpdatePlayerType();
            return gameInfo.betting;
        }

        public override void RoundReadyEvent()
        {
        }


        public override HandedCard RoundTicEvent()
        {
            try
            {
                bossData.InitData(gameInfo);
                bossData.UpdateRoundRanking();
                bossData.UpdateMyCard();
                bossData.UpdatePlayerType();
                bossData.GuessPlayerCard();
                bossData.GuessPlayerType();
                bossData.UpdateCardRisk();

                if (bossData.playerType == PlayerType.AttackPlayer) return RoundTicEventAttackPlayer();
                else if (bossData.playerType == PlayerType.FriendPlayer) return RoundTicEventAttackPlayer();
                else if (bossData.playerType == PlayerType.DependPlayer) return RoundTicEventDependPlayer();

            }
            catch (Exception)
            {

            }
            return null;
        }

        /// <summary>
        /// 공격을 한다
        /// </summary>
        /// <returns></returns>
        public HandedCard RoundTicEventAttackPlayer()
        {
            HandedCard h = null;

            if (gameInfo.roundCardIdx == 0)
            {
                if (gameInfo.roundIdx == 0)
                {
                    h = bossData.DrawCard(DrawCardType.HighestCard);
                    if (h != null) return h;
                    h = bossData.DrawCard(DrawCardType.LowCard);
                    if (h != null) return h;
                }
                else if (gameInfo.roundIdx == 8)
                {
                    h = bossData.DrawCard(DrawCardType.Joker);
                    if (h != null) return h;
                }
                if (bossData.CountCard(CountCardType.DependPlayerGiruda) > 0)
                {
                    h = bossData.DrawCard(DrawCardType.HighestGiruda);
                    if (h != null) return h;

                    if (gameInfo.friend.howToSelect != FriendPlayer.HowToSelect.nofriend && gameInfo.friend.position == gameInfo.myInfo.position)
                    {
                        h = bossData.DrawCard(DrawCardType.HighGiruda);
                        if (h != null) return h;
                    }
                    else
                    {
                        h = bossData.DrawCard(DrawCardType.Joker);
                        if (h != null) return h;

                        if (bossData.CountCard(CountCardType.MyGiruda) > 1 || (bossData.CountCard(CountCardType.MyGiruda) == 1 && bossData.CountCard(CountCardType.MySpecial) > 0))
                        {
                            h = bossData.DrawCard(DrawCardType.LowGiruda);
                            if (h != null) return h;
                        }
                        else
                        {
                            h = bossData.DrawCard(DrawCardType.HighestCard);
                            if (h != null) return h;
                        }
                    }
                }
                else
                {
                    if (bossData.CountCard(CountCardType.MyGiruda) + bossData.CountCard(CountCardType.MySpecial) + bossData.CountCard(CountCardType.MyHighestCard) == 10 - gameInfo.roundIdx)
                    {
                        h = bossData.DrawCard(DrawCardType.HighestCard);
                        if (h != null) return h;
                        h = bossData.DrawCard(DrawCardType.HighGiruda);
                        if (h != null) return h;
                        h = bossData.DrawCard(DrawCardType.Joker);
                        if (h != null) return h;
                        h = bossData.DrawCard(DrawCardType.Mighty);
                        if (h != null) return h;
                    }
                    else
                    {
                        h = bossData.DrawCard(DrawCardType.HighestCard);
                        if (h != null) return h;
                    }
                }
            }
            else
            {
                if (gameInfo.roundIdx == 8)
                {
                    h = bossData.DrawCard(DrawCardType.Joker);
                    if (h != null) return h;
                    if (bossData.CountCard(CountCardType.DependPlayerJoker) == 1)
                    {
                        h = bossData.DrawCard(DrawCardType.Mighty);
                        if (h != null) return h;
                    }
                }

                RoundCard exAttackPlayerCard = null;
                for (int i = 0; i < gameInfo.roundCardIdx; i++)
                {
                    if (gameInfo.attackPlayer.position == gameInfo.myInfo.position)
                    {
                        if (bossData.friendPosition != -1 && gameInfo.round.cardStack[i].owner == bossData.friendPosition) exAttackPlayerCard = gameInfo.round.cardStack[i]; // 주공일때 프랜드가 파트너
                    }
                    else
                    {
                        if (gameInfo.round.cardStack[i].owner == gameInfo.attackPlayer.position) exAttackPlayerCard = gameInfo.round.cardStack[i]; // 프랜드일때 주공이 파트너
                    }
                }
                if (exAttackPlayerCard != null)
                {

                    Card highestCard = bossData.FindRoundHighestCard();
                    if (highestCard.IsSame(exAttackPlayerCard) || bossData.GetRoundRanking(exAttackPlayerCard) < bossData.GetRoundRanking(highestCard))
                    {
                        h = bossData.DrawCard(DrawCardType.Score);
                        if (h != null) return h;
                        h = bossData.DrawCard(DrawCardType.LowCard);
                        if (h != null) return h;
                    }
                    else
                    {
                        h = bossData.DrawCard(DrawCardType.HighestCard);
                        if (h != null) return h;
                    }
                }
                else
                {
                    h = bossData.DrawCard(DrawCardType.HighestCard);
                    if (h != null) return h;
                }

                if (gameInfo.roundCardIdx == 4 || (gameInfo.roundCardIdx == 3 && bossData.friendPosition == (gameInfo.myInfo.position + 1) % 5))
                {
                    h = bossData.DrawCard(DrawCardType.LowWinCard);
                    if (h != null) return h;
                }
                if (bossData.CountCard(CountCardType.RoundScore) > 0)
                {
                    if (bossData.CountCard(CountCardType.RemainDependPlayer) + bossData.CountCard(CountCardType.RoundScore) > 1)
                    {
                        if (bossData.CountCard(CountCardType.MyGiruda) > 1)
                        {
                            h = bossData.DrawCard(DrawCardType.LowGiruda);
                            if (h != null) return h;
                        }
                        h = bossData.DrawCard(DrawCardType.Joker);
                        if (h != null) return h;
                        h = bossData.DrawCard(DrawCardType.Mighty);
                        if (h != null) return h;
                    }
                }
            }
            h = bossData.DrawCard(DrawCardType.LowCard);
            if (h != null) return h;
            h = bossData.DrawCard(DrawCardType.Joker);
            if (h != null) return h;
            h = bossData.DrawCard(DrawCardType.Mighty);
            if (h != null) return h;

            return null;
        }


        /// <summary>
        /// 수비를 한다
        /// </summary>
        /// <returns></returns>
        public HandedCard RoundTicEventDependPlayer()
        {
            HandedCard h = null;

            Round round = gameInfo.round;
            RoundCard firstCard = null;
            if (gameInfo.roundCardIdx != 0) firstCard = gameInfo.round.cardStack[0];
            Shape firstShape = Shape.Clover;
            if (gameInfo.roundCardIdx != 0) {
                if (firstCard.shape == Shape.Joker) firstShape = firstCard.jokerTarget;
                else firstShape = firstCard.shape;
            }
            int giruda = (int)gameInfo.betting.giruda;
            if ( giruda == 4 ) giruda = (int)firstShape;

            // 선을 잡았을 경우 프랜드를 찾아 낸다. 
            if (gameInfo.roundCardIdx == 0)
            {
                if (gameInfo.friend.howToSelect == FriendPlayer.HowToSelect.card && !gameInfo.friend.founded)
                {
                    if (gameInfo.friend.cardCondition.shape == Shape.Joker)
                    {
                        h = GetJokerCall();
                        if (h != null) return h;
                    }
                    else if (gameInfo.friend.cardCondition.IsSame(gameInfo.mighty))
                    {
                        h = GetMightyCall();
                        if (h != null) return h;
                    }
                }
            }

            // score카드가 2개 이상이고 윈카드를 가지고 있다면 낸다
            if (CountRoundScoreCard(round) >= 2)
            {
                // 무늬의 윈카드
                h = GetShapeWinCard(firstShape);
                if (h != null) return h;

                // 기루다
                h = GetWinableCard(firstShape, false);
                if (h != null) return h;

                // 조커나 마이티
                h = GetWinableCard(firstShape, true);
                if (h != null) return h;
            }

            // 주공이 먼저 했을 경우와 안했을 경우로 나누어서 윈카드, 스코어카드, 쓰레기카드를 낸다. 
            bool attackIsOver = CheckRoundPlaying(gameInfo.attackPlayer.position);
            bool friendIsOver = true;
            if ( gameInfo.friend.founded ) {
                friendIsOver = CheckRoundPlaying(gameInfo.friend.position);
            } 
            if ( attackIsOver ) {
                // [주공이 했다]

                // 누가 이기는지 찾는다
                PlayerType winner = PlayerType.DependPlayer;    
                RoundCard winCard = GetRoundWinCard(round);
                if (winCard != null)
                {
                    if (winCard.owner == gameInfo.attackPlayer.position) winner = PlayerType.AttackPlayer;
                    else if (winCard.owner == gameInfo.friend.position && gameInfo.friend.founded) winner = PlayerType.AttackPlayer;
                }

                // 주공이이기고 있다면 더 높은 카드를 내고, 수비가 이기고 있다면 스코어카드를 낸다. 
                if (winner == PlayerType.AttackPlayer)
                {
                    // [주공이 이겻다]
                    h = GetUpperCard(firstShape, winCard);
                    if (h != null) return h;
                }
                else
                {
                    // [수비가 이겼다]
                    h = GetScoreCard(firstShape);
                    if (h != null) return h;
                }

            } else {
                // [주공이 안했다]

                RoundCard winCard = GetRoundWinCard(round);

                // 첫번째 무늬를 주공이 가지고 있을 것 같다면 무늬의 윈카드를 낸다
                BossGuess attackGuess = bossData.guess[gameInfo.attackPlayer.position];
                if (attackGuess.extShape[(int)firstShape] > 0.5)
                {
                    // [첫번째 무늬가 있다]
                    h = GetShapeWinCard(firstShape);
                    if (h != null) return h;
                } else {
                    // [첫번째 무늬가 없다]
                    if (attackGuess.extShape[giruda] < 0.5)
                    {
                        // [기루다가 없다]
                        h = GetShapeWinCard(firstShape);
                        if (h != null) return h;
                    }
                }
            }

            h = GetNoneCard(firstShape);
            if (h != null) return h;

            return null;
        }

        /// <summary>
        /// 해당라운드의 wincard를 돌려준다
        /// </summary>
        /// <param name="round"></param>
        /// <returns></returns>
        private RoundCard GetRoundWinCard(Round round)
        {
            RoundCard winCard = null;
            if (round.cardStack[0] == null) return winCard;

            int position = -1;
            int ranking = 99;

            for (int i = 0; i < 5; i++)
            {
                RoundCard rc = round.cardStack[i];
                if (rc == null) continue;

                int r = bossData.GetRoundRanking(rc);
                if (r < ranking)
                {
                    ranking = r;
                    position = i;
                }
            }

            if (position == -1) throw new Exception();

            return round.cardStack[position];
        }

        /// <summary>
        /// 해당 라운드에서 몇개의 score card가 있는지 계산한다
        /// </summary>
        /// <param name="round"></param>
        /// <returns></returns>
        private int CountRoundScoreCard(Round round)
        {
            int cnt = 0;
            Round r = gameInfo.round;
            for (int i = 0; i < 5; i++)
            {
                RoundCard rc = r.cardStack[i];
                if (rc == null) continue;

                if (rc.number >= 10) cnt++;
            }
            return cnt;
        }

        /// <summary>
        /// 특정 player가 했는지 안했는지 확인한다
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private bool CheckRoundPlaying(int p)
        {
            Round r = gameInfo.round;

            bool isOver = false;
            for (int i = 0; i < 5; i++)
            {
                RoundCard rc = r.cardStack[i];
                if (rc == null) continue;

                if (rc.owner == p) isOver = true;
            }

            return isOver;
        }

        /// <summary>
        /// 해당 card의 risk를 계산한다. 
        /// * 마이티, 조커, 기루다 윈카드는 0 이다
        /// * 자신의 카드를 이길 수 있는 카드개수를 계산한다. 
        /// * 기루다카드와 해당무늬의 상위카드가 포함된다
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public int GetCardRisk(Card c)
        {
            int risk=0;
            if (c.shape != Shape.Joker || c.IsSame(gameInfo.mighty)) return risk;

            // giruda에 의한 risk
            if (gameInfo.betting.giruda != Giruda.NoGiruda && (int)c.shape != (int)gameInfo.betting.giruda )
            {
                for (int i = 14; i > 1; i--)
                {
                    if (bossData.usedCard[(int)gameInfo.betting.giruda, i] == UsedType.none ) risk++;
                }
            }
            risk += GetCardRiskShape(c);
            return risk;
        }

        /// <summary>
        /// 해당 card의 무늬중에서의 risk를 구한다
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public int GetCardRiskShape(Card c)
        {
            int risk = 0;
            if (c.shape == Shape.Joker) return risk;

            for (int i = 14; i > 1; i--)
            {
                if (i <= c.number) break;
                if (bossData.usedCard[(int)c.shape, i] == UsedType.none ) risk++;
            }
            return risk;
        }

        /// <summary>
        /// 조커프랜드일때 선을 잡고 조커콜을 한다
        /// </summary>
        /// <returns></returns>
        public HandedCard GetJokerCall()
        {
            if (bossData.usedCard[(int)gameInfo.jokerCall.shape, gameInfo.jokerCall.number] == UsedType.have)
            {
                int idx = bossData.indexCard[(int)gameInfo.jokerCall.shape, gameInfo.jokerCall.number];
                return gameInfo.myCards[idx];
            }
            return null;
        }

        /// <summary>
        /// 마이티프랜드일때 마이티무늬 낮은 그림을 순차적으로 낸다
        /// </summary>
        /// <returns></returns>
        public HandedCard GetMightyCall()
        {
            int shape = (int)gameInfo.mighty.shape;
            IList<int> keys = bossData.sortedCard[shape].Keys;
            if (keys.Count > 0)
            {
                return bossData.sortedCard[shape][keys[0]];
            }
            return null;
        }

        /// <summary>
        /// 내가 가지는 카드 중에서 해당 카드를 최소한으로 이기는 카드를 낸다
        /// * 조커와 마이티는 포함안된다
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="winCard"></param>
        /// <returns></returns>
        private HandedCard GetUpperCard(Shape shape, RoundCard winCard)
        {
            if (winCard == null) throw new Exception();
            int winRanking = bossData.GetRoundRanking(winCard);

            int index = -1;
            int ranking = -1;
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (!h.isPossible) continue;
                if (h.shape == Shape.Joker) continue;
                if (h.shape == gameInfo.mighty.shape && h.number == gameInfo.mighty.number) continue;


                int r = bossData.GetRoundRanking(h);
                if (r < winRanking)
                {
                    if (r > ranking)
                    {
                        ranking = r;
                        index = i;
                    }
                }
            }

            if (index == -1) return null;
            return gameInfo.myCards[index];
        }

        /// <summary>
        /// 묻어주기 좋은카드 (미완성)
        /// * 10이상의 카드
        /// * 윈카드의 risk가 +2이상인 점수카드 (자신이 가지고 있으면 risk에 포함안되므로, 이것의 의미는 자신을 먹을 수 있는 카드가 2개 이상있다는 이야기)
        /// * risk가 높을 수록 묻어주기 좋다
        /// * risk가 동일하다면 개수가 적을 수록 묻는게 낳다
        /// </summary>
        public HandedCard GetScoreCard(Shape firstShape)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (!h.isPossible) continue;

                if (firstShape == h.shape)
                {
                    int risk = GetCardRiskShape(h);
                    if (risk >= 2 && h.number >= 10) return h;
                }
            }
            return null;
        }

        /// <summary>
        /// 해당무늬중에서 가장작은카드
        /// </summary>
        /// <param name="shape"></param>
        /// <returns></returns>
        public HandedCard GetLowestCard(Shape shape)
        {
            if (shape == Shape.Joker) return null;

            int num = 15;
            int idx = -1;
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (!h.isPossible) continue;

                if (h.shape == shape && h.number < num )
                {
                    num = h.number;
                    idx = i;
                }
            }
            if (idx != -1) return gameInfo.myCards[idx];
            return null;
        }

        /// <summary>
        /// 해당무늬중에서 가장높은카드
        /// </summary>
        /// <param name="shape"></param>
        /// <returns></returns>
        public HandedCard GetHighestCard(Shape shape)
        {
            if (shape == Shape.Joker) return null;

            int num = 1;
            int idx = -1;
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (!h.isPossible) continue;

                if (h.shape == shape && h.number > num)
                {
                    num = h.number;
                    idx = i;
                }
            }
            if (idx != -1) return gameInfo.myCards[idx];
            return null;
        }

        /// <summary>
        /// 이길만한 카드
        /// * 현재무늬가 없을 경우 낮은 기루다 카드
        /// * 조커, 마이티 (special 일때만)
        /// </summary>
        /// <param name="firstShape"></param>
        /// <returns></returns>
        public HandedCard GetWinableCard(Shape firstShape, bool special)
        {
            if ( gameInfo.betting.giruda != Giruda.NoGiruda ) {
                Shape giruda = (Shape)gameInfo.betting.giruda;

                if ( firstShape != giruda) {
                    if ( CountRemainShape(firstShape) == 0 ) {
                        HandedCard h = GetLowestCard(giruda);
                        if ( h != null && h.number < 10 ) return h;
                    }
                }
            }

            if (special)
            {
                for (int i = 0; i < 10; i++)
                {
                    HandedCard h = gameInfo.myCards[i];
                    if (!h.isPossible) continue;
                    if (h.shape == Shape.Joker) return h;
                }

                for (int i = 0; i < 10; i++)
                {
                    HandedCard h = gameInfo.myCards[i];
                    if (!h.isPossible) continue;
                    if (h.shape == gameInfo.mighty.shape && h.number == gameInfo.mighty.number) return h;
                }
            }

            return null;
        }

        /// <summary>
        /// 전체의 윈카드
        /// * risk가 0인것들
        /// * joker와 mighty는 제외하고
        /// * 주공일때 주로 사용한다. (기루다 위주로 내는 역활을 한다)
        /// </summary>
        public HandedCard GetWinCard(Shape firstShape)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (!h.isPossible) continue;

                if (firstShape == h.shape)
                {
                    int risk = GetCardRisk(h);
                    if (risk == 0) return h;
                }
            }
            return null;
        }

        /// <summary>
        /// 무늬의 윈카드
        /// * risk가 0인것들
        /// </summary>
        public HandedCard GetShapeWinCard(Shape firstShape)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (!h.isPossible) continue;

                if (firstShape == h.shape)
                {
                    int risk = GetCardRiskShape(h);
                    if (risk == 0) return h;
                }
            }
            return null;
        }

        /// <summary>
        /// 버릴카드 
        /// * 기루다는 남긴다
        /// * 기루다가 있다면 무늬가 적은카드를 버린다 (미완성)
        /// * 기루다가 없다면 risk가 낮은카드를 남긴다
        /// </summary>
        /// <param name="firstShape"></param>
        /// <returns>버릴카드</returns>
        public HandedCard GetNoneCard(Shape firstShape)
        {
            // 해당무늬가 있다면
            int index = -1;
            int ranking = 0;
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (!h.isPossible) continue;

                if (firstShape == h.shape)
                {
                    int r = bossData.GetRoundRanking(h);
                    if (r > ranking)
                    {
                        index = i;
                        ranking = r;
                    }
                }
            }

            // 해당무늬가 없다면
            if (index == -1)
            {
                for (int i = 0; i < 10; i++)
                {
                    HandedCard h = gameInfo.myCards[i];
                    if (!h.isPossible) continue;

                    int r = bossData.GetRoundRanking(h);
                    if (r > ranking)
                    {
                        index = i;
                        ranking = r;
                    }
                }
            }

            // 그래도 없다면 제일먼저선택된 카드를 낸다
            if (index == -1)
            {
                for (int i = 0; i < 10; i++)
                {
                    HandedCard h = gameInfo.myCards[i];
                    if (!h.isPossible) continue;

                    return h;
                }
            }

            if (index == -1) return null;
            return gameInfo.myCards[index];
        }

        public int CountRemainShape(Shape s)
        {
            int cnt = 0;
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (!h.isPossible) continue;

                if (h.shape == s) cnt++;
            }
            return cnt;
        }

        public override void RoundCompleteEvent()
        {
        }
    }
}
